package com.floreantpos.services;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.floreantpos.main.Application;
import com.floreantpos.model.ActionHistory;
import com.floreantpos.model.CashTransaction;
import com.floreantpos.model.CreditCardTransaction;
import com.floreantpos.model.DebitCardTransaction;
import com.floreantpos.model.GiftCertificateTransaction;
import com.floreantpos.model.Gratuity;
import com.floreantpos.model.Guest;
import com.floreantpos.model.MenuItem;
import com.floreantpos.model.PosTransaction;
import com.floreantpos.model.RefundTransaction;
import com.floreantpos.model.Terminal;
import com.floreantpos.model.Ticket;
import com.floreantpos.model.TicketItem;
import com.floreantpos.model.User;
import com.floreantpos.model.dao.ActionHistoryDAO;
import com.floreantpos.model.dao.GenericDAO;
import com.floreantpos.model.dao.GratuityDAO;
import com.floreantpos.model.dao.GuestDAO;
import com.floreantpos.model.dao.MenuItemDAO;
import java.text.NumberFormat;
import java.util.Locale;

public class PosTransactionService {

  private static PosTransactionService paymentService = new PosTransactionService();

  public void settleTickets(List<Ticket> tickets, double tenderedAmount, double gratuityAmount, PosTransaction transaction, String cardType, String cardAuthorizationCode, String taxId) throws Exception {
    Application application = Application.getInstance();
    User currentUser = Application.getCurrentUser();
    Terminal terminal = application.getTerminal();

    Session session = null;
    Transaction tx = null;

    GenericDAO dao = new GenericDAO();

    try {
      Date currentDate = new Date();
      boolean gratuityPaid = false;

      session = dao.getSession();
      tx = session.beginTransaction();

      double totalDueAmount = 0;
      double cashBackAmount = 0;

      for (Ticket ticket : tickets) {
        totalDueAmount += ticket.getDueAmount();
      }

      cashBackAmount = tenderedAmount - totalDueAmount;

      if (cashBackAmount < 0.01) {
        cashBackAmount = 0;
      }

      for (Iterator it = tickets.iterator(); it.hasNext();) {
        Ticket ticket = (Ticket) it.next();

        ticket.setVoided(false);
        ticket.setDrawerResetted(false);
        ticket.setClosingDate(currentDate);
        ticket.setTerminal(terminal);
        ticket.setCardNumber(cardAuthorizationCode.substring(0, cardAuthorizationCode.length() - 1));
        ticket.setTransactionSubtype(cardAuthorizationCode.substring(cardAuthorizationCode.length() - 1));
        ticket.setTaxId(taxId);
        
        GuestDAO guestDAO = GuestDAO.getInstance();
        Guest registeredGuest = null;
        
        if (taxId != null) {
          if (ticket.getRegisteredGuest() == null) {
            registeredGuest = guestDAO.findGuestByTaxId(taxId, false);
            if (registeredGuest != null) {
              String guestName = registeredGuest.getName();
              String notes = registeredGuest.getTelephone() + "|" + registeredGuest.getAddressLine1();

              if (!registeredGuest.getAddressLine2().isEmpty()) {
                notes = notes + " - " + registeredGuest.getAddressLine2();
              }

              if (!registeredGuest.getAddressLine3().isEmpty()) {
                notes = notes + " - " + registeredGuest.getAddressLine3();
              }

              String ticketNotes = ticket.getNotes();

              if (ticketNotes == null) {
                notes = notes + "|";
              } else {
                notes = notes + "|" + ticket.getNotes();
              }

              ticket.setRegisteredGuest(registeredGuest.getId());
              ticket.setGuest(guestName);
              ticket.setNotes(notes);              
            }
          }
        }
        
        List<TicketItem> ticketItems = ticket.getTicketItems();
        if (ticketItems != null) {
          MenuItemDAO menuItemDAO = MenuItemDAO.getInstance();
          for (TicketItem ticketItem : ticketItems) {
            MenuItem menuItem = menuItemDAO.get(ticketItem.getItemId(), session);
            if (menuItem != null) {
              if (menuItem.isUpdateInventory()) {
                menuItem.setQtyOnHand(menuItem.getQtyOnHand() - ticketItem.getItemCount());
              }              
            }
          }
        }
        
        NumberFormat numberFormat = NumberFormat.getInstance(new Locale("pt", "BR"));
        double paidAmount = numberFormat.parse(Application.formatNumber(ticket.getPaidAmount())).doubleValue();
        double dueAmount = numberFormat.parse(Application.formatNumber(ticket.getDueAmount())).doubleValue();

        if ((tenderedAmount - gratuityAmount) >= dueAmount) {
          paidAmount += dueAmount;
          dueAmount = 0;

          ticket.setPaid(true);
          ticket.setClosed(true);
        } else {
          paidAmount += (tenderedAmount - gratuityAmount);
          dueAmount -= (tenderedAmount - gratuityAmount);

          if (dueAmount < 0.01) {
            dueAmount = 0;
            ticket.setPaid(true);
            ticket.setClosed(true);
          } else {
            ticket.setPaid(false);
            ticket.setClosed(false);
          }
        }
        ticket.setPaidAmount(paidAmount);
        ticket.setDueAmount(dueAmount);

        if (!gratuityPaid && gratuityAmount > 0) {
          if (ticket.getGratuity() != null && !ticket.getGratuity().isPaid() && !ticket.isReOpened()) {
            ticket.getGratuity().setAmount(ticket.getGratuity().getAmount() + gratuityAmount);
          } else if (ticket.getGratuity() != null && !ticket.getGratuity().isPaid() && ticket.isReOpened()) {
            ticket.getGratuity().setAmount(gratuityAmount);
          } else {
            Gratuity gratuity = new Gratuity();
            gratuity.setAmount(gratuityAmount);
            gratuity.setOwner(ticket.getOwner());
            gratuity.setPaid(false);
            gratuity.setTicket(ticket);
            gratuity.setTerminal(ticket.getTerminal());
            ticket.setGratuity(gratuity);
          }
          gratuityPaid = true;
        }

        PosTransaction posTransaction = null;
        if (transaction instanceof CashTransaction) {
          posTransaction = new CashTransaction();

          Double currentBalance = terminal.getCurrentBalance();
          double newBalance = currentBalance + tenderedAmount;

          terminal.setCurrentBalance(newBalance);

          ticket.setTransactionType(PosTransaction.TYPE_CASH);
        } else if (transaction instanceof CreditCardTransaction) {
          posTransaction = new CreditCardTransaction();
          ((CreditCardTransaction) posTransaction).setCardNumber(cardAuthorizationCode.substring(0, cardAuthorizationCode.length() - 1));
          ((CreditCardTransaction) posTransaction).setCardType(cardType);
          ticket.setCardType(cardType);
          ticket.setTransactionType(PosTransaction.TYPE_CREDIT_CARD);
          ticket.setTransactionSubtype(cardAuthorizationCode.substring(cardAuthorizationCode.length() - 1));
        } else if (transaction instanceof DebitCardTransaction) {
          posTransaction = new DebitCardTransaction();
          ((DebitCardTransaction) posTransaction).setCardNumber(cardAuthorizationCode.substring(0, cardAuthorizationCode.length() - 1));
          ((DebitCardTransaction) posTransaction).setCardType(cardType);
          ticket.setCardType(cardType);
          ticket.setTransactionType(PosTransaction.TYPE_DEBIT_CARD);
          ticket.setTransactionSubtype(cardAuthorizationCode.substring(cardAuthorizationCode.length() - 1));
        } else if (transaction instanceof GiftCertificateTransaction) {
          posTransaction = transaction;
          GiftCertificateTransaction giftCertificateTransaction = (GiftCertificateTransaction) posTransaction;

          Double currentBalance = terminal.getCurrentBalance();
          double newBalance = currentBalance - giftCertificateTransaction.getCashBackAmount();
          terminal.setCurrentBalance(newBalance);

          ticket.setTransactionType(PosTransaction.TYPE_GIFT_CERT);
        }

        posTransaction.setTicket(ticket);
        posTransaction.setSubtotalAmount(ticket.getSubtotalAmount());
        posTransaction.setDiscountAmount(ticket.getDiscountAmount());
        posTransaction.setTaxAmount(ticket.getTaxAmount());
        posTransaction.setTotalAmount(ticket.getTotalAmount());

        if (ticket.getGratuity() != null) {
          posTransaction.setGratuityAmount(ticket.getGratuity().getAmount());
        }

        posTransaction.setTerminal(terminal);
        posTransaction.setUser(currentUser);
        posTransaction.setTransactionTime(currentDate);
        
        
        if (ticket.getRegisteredGuest() != null) {
          registeredGuest = guestDAO.get(ticket.getRegisteredGuest(), session);
          if (registeredGuest != null){
            if (ticket.getTransactionType().equals(PosTransaction.TYPE_GIFT_CERT)) {
              registeredGuest.setCurrentBalance(registeredGuest.getCurrentBalance() - paidAmount - gratuityAmount);
            } else if (ticket.isPaymentTicket()){
              registeredGuest.setCurrentBalance(registeredGuest.getCurrentBalance() + paidAmount);
            }            
          }          
        }

        dao.saveOrUpdate(ticket, session);
        dao.saveOrUpdate(posTransaction, session);
        dao.saveOrUpdate(terminal, session);
      }

      tx.commit();
    } catch (Exception e) {
      try {
        tx.rollback();
      } catch (Exception x) {
      }
      throw e;
    } finally {
      dao.closeSession(session);
    }

    for (Ticket ticket : tickets) {
      String actionMessage = com.floreantpos.POSConstants.CHK_NO + ticket.getId();
      actionMessage += ";" + com.floreantpos.POSConstants.TOTAL + ": " + Application.formatNumber(ticket.getTotalAmount());
      ActionHistoryDAO.getInstance().saveHistory(Application.getCurrentUser(), ActionHistory.SETTLE_CHECK, actionMessage);
    }
  }

  public void refundTicket(Ticket ticket) throws Exception {
    Application application = Application.getInstance();
    User currentUser = Application.getCurrentUser();
    Terminal terminal = application.getTerminal();

    Session session = null;
    Transaction tx = null;

    GenericDAO dao = new GenericDAO();

    Gratuity gratuityReversal = null;
    Boolean isGratuityReversalCreated = false;
    Boolean isGratuityReversalUpdated = false;

    try {
      Double currentBalance = terminal.getCurrentBalance();
      Double totalPrice = Double.valueOf(0);
      if (ticket.getGratuity() != null) {
        if (ticket.getGratuity().isPaid()) {
          GratuityDAO gratuityDAO = new GratuityDAO();
          List<Gratuity> gratuities = gratuityDAO.findGratuityByTicket(ticket);
          for (Gratuity gratuity : gratuities) {
            if (gratuity.getId().intValue() != ticket.getGratuity().getId().intValue()) {
              gratuityReversal = gratuity;
              if (gratuityReversal.isPaid()) {
                if (Application.getGratuityRate() != 0) {
                  gratuityReversal.setAmount(Double.valueOf(0));
                } else {
                  gratuityReversal.setAmount(ticket.getGratuity().getAmount() * -1);
                }
                gratuityReversal.setPaid(false);
                gratuityReversal.setDrawerResetted(false);
              } else {
                gratuityReversal.setAmount(ticket.getGratuity().getAmount() * -1);
              }
              isGratuityReversalUpdated = true;
            }
          }
          if (!isGratuityReversalUpdated) {
            gratuityReversal = new Gratuity();
            gratuityReversal.setAmount(ticket.getGratuity().getAmount() * -1);
            gratuityReversal.setOwner(ticket.getOwner());
            gratuityReversal.setPaid(false);
            gratuityReversal.setTicket(ticket);
            gratuityReversal.setTerminal(ticket.getTerminal());
            isGratuityReversalCreated = true;
          }
        }
        totalPrice = ticket.getTotalAmount() + ticket.getGratuity().getAmount();
        ticket.getGratuity().setPaid(false);
        ticket.getGratuity().setDrawerResetted(false);

        if (Application.getGratuityRate() != 0) {
          ticket.getGratuity().setAmount(Double.valueOf(0));
        }
      } else {
        totalPrice = ticket.getTotalAmount();
      }

      if (ticket.getTransactionType().equals(PosTransaction.TYPE_CASH)) {
        double newBalance = currentBalance - totalPrice;
        terminal.setCurrentBalance(newBalance);
      }

      ticket.setVoided(false);
      ticket.setPaid(false);
      ticket.setClosed(false);
      ticket.setDrawerResetted(false);
      ticket.setClosingDate(null);
      ticket.setReOpened(true);
      ticket.setTerminal(terminal);
      ticket.setDueAmount(ticket.getTotalAmount());
      ticket.setPaidAmount(Double.valueOf(0));

      RefundTransaction posTransaction = new RefundTransaction();
      posTransaction.setTicket(ticket);

      posTransaction.setSubtotalAmount(ticket.getSubtotalAmount());
      posTransaction.setDiscountAmount(ticket.getDiscountAmount());
      posTransaction.setTaxAmount(ticket.getTaxAmount());
      posTransaction.setTotalAmount(totalPrice);

      posTransaction.setTerminal(terminal);
      posTransaction.setUser(currentUser);
      posTransaction.setTransactionTime(new Date());

      session = dao.getSession();
      tx = session.beginTransaction();
      
      List<TicketItem> ticketItems = ticket.getTicketItems();
      if (ticketItems != null) {
        MenuItemDAO menuItemDAO = MenuItemDAO.getInstance();
        for (TicketItem ticketItem : ticketItems) {
          MenuItem menuItem = menuItemDAO.get(ticketItem.getItemId(), session);
          if (menuItem.isUpdateInventory()) {
            menuItem.setQtyOnHand(menuItem.getQtyOnHand() + ticketItem.getItemCount());
          }
        }
      }      

      if (isGratuityReversalCreated || isGratuityReversalUpdated) {
        dao.saveOrUpdate(gratuityReversal, session);
      }
      dao.saveOrUpdate(ticket, session);
      dao.saveOrUpdate(posTransaction, session);
      dao.saveOrUpdate(terminal, session);

      tx.commit();

    } catch (Exception e) {
      try {
        tx.rollback();
      } catch (Exception x) {
      }

      throw e;
    } finally {
      dao.closeSession(session);
    }

  }

  public static PosTransactionService getInstance() {
    return paymentService;
  }
}
